package xx.filter;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import xx.entity.manage.Account;
import xx.entity.manage.Role;
import xx.services.util.Constants;
import xx.services.util.Online;
import xx.services.util.Results;
import xx.util.string.Format;

import com.sun.jersey.spi.container.servlet.ServletContainer;

/**
 * 验证URI是否有访问权限
 * 
 * @author chenxiang
 */
public class SecurityFilter extends ServletContainer
{
    private Log logger = LogFactory.getLog(this.getClass());

    private String rootPath = null;

    /** 需要做登陆验证的get请求的uri，默认get请求都不需要登陆验证 */
    private ArrayList<String> getExcludeList = new ArrayList<String>();

    /** 不需要做登陆验证的post请求的uri，默认post请求都需要登陆验证 */
    private ArrayList<String> postExcludeList = new ArrayList<String>();

    /** 不需要做登陆验证的put请求的uri，默认put请求都需要登陆验证 */
    private ArrayList<String> putExcludeList = new ArrayList<String>();

    /** 不需要做登陆验证的delete请求的uri，默认delete请求都需要登陆验证 */
    private ArrayList<String> deleteExcludeList = new ArrayList<String>();

    /** 私有写操作请求uri，只有自己或者权限比自己高的人可以执行 */
    private ArrayList<String> privateExcludeList = new ArrayList<String>();

    private static final long serialVersionUID = 1L;

    private static final String GET = "get";

    private static final String POST = "post";

    private static final String PUT = "put";

    private static final String DELETE = "delete";

    private static final String JSESSIONID = "JSESSIONID";

    @Override
    public void destroy()
    {
        getExcludeList.clear();
        postExcludeList.clear();
        putExcludeList.clear();
        deleteExcludeList.clear();
        privateExcludeList.clear();
        super.destroy();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;

        // 不要包含context-path
        String uriPattern = req.getServletPath();
        if (req.getPathInfo() != null)
            uriPattern = uriPattern + req.getPathInfo();

        // 后台管理
        if (uriPattern.matches("/(services|servlet)/(.)*"))
        {
            request.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=utf-8");

            // 过虑非当请求（参数中包含非法关键字等请求为非法请求）
            if (this.filterRequest(req))
            {
                Results results = new Results();
                response.getWriter().print(results.error("非法访问！"));
                return;
            }

            // chain.doFilter(request, response);
            // if (true)
            // return;

            // 需要登陆才能访问
            // if (uriPattern.indexOf("/manage/") > 0 || uriPattern.indexOf("/management/") > 0)
            String referer = req.getHeader("referer");
            if (isPrivate(uriPattern, req.getMethod(), referer))
            {
                if (isLogin(req))
                {
                    if (!GET.equalsIgnoreCase(req.getMethod()))
                    {
                        if (!isWrite(req))
                        {
                            Results results = new Results();
                            response.getWriter().print(results.error("无此操作权限，请联系管理员！"));
                            return;
                        }
                    }
                    super.doFilter(request, response, chain);
                }
                else
                {
                    Results results = new Results();
                    response.getWriter().print(results.timeout());
                }
            }
            // 不需要登陆访问
            else
            {
                super.doFilter(request, response, chain);
            }
            return;
        }

        super.doFilter(request, response, chain);
    }

    private boolean isWrite(HttpServletRequest req)
    {
        String jsessionId = req.getParameter(JSESSIONID);
        HttpSession session = jsessionId == null ? req.getSession() : Online.getSession(jsessionId);
        if (session == null)
            return false;

        Account account = (Account) session.getAttribute(Constants.ACCOUNT);
        if (account == null)
            return false;

        try
        {
            // 不要包含context-path
            String uri = req.getServletPath();
            if (req.getPathInfo() != null)
                uri = uri + req.getPathInfo();
            for (String str : privateExcludeList)
            {
                Pattern p = Pattern.compile(str);
                Matcher m = p.matcher(uri);
                if (m.find())
                {
                    long userId = Long.valueOf(m.group(2));
                    if (userId == account.getUserId())
                        return true;
                }
            }

            Role role = account.getRole();
            return role.isWrite(req.getMethod());
        }
        catch (SQLException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }

    private boolean isLogin(HttpServletRequest req)
    {
        String jsessionId = req.getParameter(JSESSIONID);
        HttpSession session = jsessionId == null ? req.getSession() : Online.getSession(jsessionId);
        if (session == null)
            return false;

        if (jsessionId == null)
        {
            return (session != null) && Format.strToBoolean("" + session.getAttribute("is_login"));
        }
        else
        {
            String forwarded = req.getHeader(Constants.X_FORWARDED_FOR);
            String host = req.getHeader(Constants.HOST);
            String agent = req.getHeader(Constants.USER_AGENT);
            boolean isLogin = Format.strToBoolean("" + session.getAttribute("is_login"));
            return isLogin;
            // return isLogin && forwarded.equals(session.getAttribute(Constants.X_FORWARDED_FOR)) && host.equals(session.getAttribute(Constants.HOST)) && agent.indexOf("Flash") >=
            // 0;
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException
    {
        rootPath = config.getInitParameter("root-path");
        initExclude(config.getInitParameter("get-exclude-paths"), getExcludeList);
        initExclude(config.getInitParameter("post-exclude-paths"), postExcludeList);
        initExclude(config.getInitParameter("put-exclude-paths"), putExcludeList);
        initExclude(config.getInitParameter("delete-exclude-paths"), deleteExcludeList);
        initExclude(config.getInitParameter("private-exclude-paths"), privateExcludeList);
        super.init(config);
    }

    /**
     * @param uri
     * @param mothed
     * @return
     */
    private boolean isPrivate(String uri, String mothed, String referer)
    {
        if (!Format.isEmpty(referer) && referer.indexOf("/management/") >= 0 && GET.equalsIgnoreCase(mothed))
        {
            return true;
        }

        ArrayList<String> excludeList = this.getExcludeList(mothed);
        if (excludeList.contains(uri))
        {
            return GET.equalsIgnoreCase(mothed);
        }
        else
        {
            for (String str : excludeList)
            {
                if (uri.matches(str))
                {
                    return GET.equalsIgnoreCase(mothed);
                }
            }
        }
        return !GET.equalsIgnoreCase(mothed);
    }

    private ArrayList<String> getExcludeList(String method)
    {
        if ("post".equalsIgnoreCase(method))
        {
            return postExcludeList;
        }
        else if ("put".equalsIgnoreCase(method))
        {
            return putExcludeList;
        }
        else if ("delete".equalsIgnoreCase(method))
        {
            return deleteExcludeList;
        }
        else
        {
            return getExcludeList;
        }
    }

    private void initExclude(String excludePaths, ArrayList<String> list)
    {
        if (excludePaths != null)
        {
            String[] paths = excludePaths.trim().split(",");
            for (String str : paths)
            {
                String s = rootPath + str.trim();
                if ("".equals(s))
                {
                    continue;
                }
                list.add(s);
            }
        }
    }

    /**
     * 过虑非法请求（参数中包含非法关键字等请求为非法请求）
     * 
     * @param request
     * @return true-包含有非法参数，false-验证通过
     * @author 陈祥 2011-9-13
     */
    @SuppressWarnings("unchecked")
    private boolean filterRequest(HttpServletRequest request)
    {
        // String keyword =
        // "INSERT#SELECT#DELETE#UPDATE#DROP#MID#TRUNCATE#DECLARE#JAVASCRIPT#<SCRIPT#3CSCRIPT#%3C#<#--#'#%27#%C6%C6%B2%FA#%B5%B9%B1%D5#%D0%B0%BD%CC#%B7%A8%C2%D6%B9%A6#%C0%EE%BA%E9%D6%BE#%BF%E1%D0%CC#%D5%DB%C4%A5#%B1%BB%B2%B6#破产#倒闭#邪教#法轮功#李洪志#酷刑#折磨#被捕";
        String keyword = "INSERT#SELECT#DELETE#UPDATE#DROP#MID#TRUNCATE#DECLARE";

        StringBuilder sb = new StringBuilder();
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements())
        {
            String key = names.nextElement();
            String value = request.getParameter(key);
            sb.append("#").append(key).append("#").append(value);
        }

        String src = sb.toString().toUpperCase();
        String[] keywords = keyword.split("#");
        for (int i = 0, l = keywords.length; i < l; i++)
        {
            if (src.indexOf(keywords[i]) >= 0)
            {
                logger.warn("SecurityFilter find security key:" + keywords[i] + "!");
                return true;
            }
        }

        return false;
    }
}
